﻿// <copyright file="Matrix.cs" company="Signal Processing Lab, Wuhan University">
// Copyright (c) 2009-2010 All Right Reserved
// </copyright>
// <author>Xinping Deng</author>
// <email>dentoby@gmail.com</email>
// <date>2010-4</date>
// <summary>This is part of Image.NET project.</summary>

namespace SPLWHU.Core.Raster
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// The summary of matrix class.
    /// </summary>
    /// <typeparam name="T">The data type.</typeparam>
    public class Matrix<T>
    {
        /// <summary>
        /// The max size of memory to allocate.
        /// </summary>
        public static readonly int MaxSize = 1000 * 1000;

        /// <summary>
        /// The width of Matrix.
        /// </summary>
        private int columns;

        /// <summary>
        /// The height of Matrix.
        /// </summary>
        private int rows;

        /// <summary>
        /// The matrix data.
        /// </summary>
        private T[][] data;

        /// <summary>
        /// Initializes a new instance of the <see cref="Matrix&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="rows">The number of rows.</param>
        /// <param name="columns">The number of columns.</param>
        protected Matrix(int rows, int columns)
        {
            this.columns = columns;
            this.rows = rows;

            // allocates memory.
            this.Allocate();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Matrix&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="data">The matrix data.</param>
        protected Matrix(T[][] data)
        {
            this.columns = data.Length;
            this.rows = data[0].Length;
            this.data = data;
        }

        #region Accessors

        /// <summary>
        /// Gets the width.
        /// </summary>
        /// <value>The width.</value>
        public int Columns
        {
            get { return this.columns; }
        }

        /// <summary>
        /// Gets the height.
        /// </summary>
        /// <value>The height.</value>
        public int Rows
        {
            get { return this.rows; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is a square.
        /// </summary>
        /// <value><c>true</c> if this instance is a square; otherwise, <c>false</c>.</value>
        public bool IsSquare
        {
            get { return this.columns == this.rows; }
        }

        /// <summary>
        /// Gets the type of the data.
        /// </summary>
        /// <value>The type of the data.</value>
        public Type DataType
        {
            get { return typeof(T); }
        }

        /// <summary>
        /// Gets or sets the value with the specified position.
        /// </summary>
        /// <param name="x">The x column index.</param>
        /// <param name="y">The y row index.</param>
        /// <returns>The value.</returns>
        public virtual T this[int x, int y]
        {
            get
            {
                try
                {
                    return this.data[y][x];
                }
                catch
                {
                    throw new MatrixException("Requires [{0}, {1}] from {2}.", x, y, this.ToString());
                }
            }

            set
            {
                try
                {
                    this.data[y][x] = value;
                }
                catch
                {
                    throw new MatrixException("Requires [{0}, {1}] from {2}.", x, y, this.ToString());
                }
            }
        }

        /// <summary>
        /// Gets or sets the row at the specified postion.
        /// </summary>
        /// <param name="y">The row index.</param>
        /// <returns>The row data.</returns>
        public virtual T[] this[int y]
        {
            get
            {
                try
                {
                    return this.data[y];
                }
                catch
                {
                    throw new MatrixException("Requires row [{0}] from {1}.", y, this.ToString());
                }
            }

            set
            {
                try
                {
                    this.data[y] = value;
                }
                catch
                {
                    throw new MatrixException("Requires row [{0}] from {1}.", y, this.ToString());
                }
            }
        }

        #endregion

        #region Matrix Creating Methods

        /// <summary>
        /// Creates a matrix width specified rows and columns.
        /// </summary>
        /// <param name="rows">The number of rows.</param>
        /// <param name="columns">The number of columns.</param>
        /// <returns>The new matrix.</returns>
        public static Matrix<T> CreateMatrix(int rows, int columns)
        {
            if (columns * rows > Matrix<T>.MaxSize)
            {
                return new VirtualMatrix<T>(columns, rows);
            }
            else
            {
                return new Matrix<T>(columns, rows);
            }
        }

        /// <summary>
        /// Creates a matrix with specifed data.
        /// </summary>
        /// <param name="data">The matrix data.</param>
        /// <returns>The new matrix.</returns>
        public static Matrix<T> CreateMatrix(T[][] data)
        {
            return new Matrix<T>(data);
        }

        /// <summary>
        /// Creates a new matrix by specifying the columns and rows.
        /// </summary>
        /// <param name="columns">The number of columns.</param>
        /// <param name="rows">The number of rows.</param>
        /// <param name="data">The matrix data.</param>
        /// <returns>The new matrix.</returns>
        public static Matrix<T> CreateMatrix(int columns, int rows, T[] data)
        {
            // create matrix.
            Matrix<T> ret = null;
            if (rows * columns > Matrix<T>.MaxSize)
            {
                ret = new VirtualMatrix<T>(rows, columns);
            }
            else
            {
                ret = new Matrix<T>(rows, columns);
            }

            // fills data.
            ret.Fill(data);

            // returns.
            return ret;
        }

        /// <summary>
        /// Creates the matrix.
        /// </summary>
        /// <param name="data">The matrix data.</param>
        /// <returns>The new matrix</returns>
        public static Matrix<T> CreateMatrix(T[,] data)
        {
            // gets rows and columns
            int rows = data.GetLength(0), columns = data.GetLength(1);

            // create matrix.
            Matrix<T> ret = null;
            if (rows * columns > Matrix<T>.MaxSize)
            {
                ret = new VirtualMatrix<T>(rows, columns);
            }
            else
            {
                ret = new Matrix<T>(rows, columns);
            }

            // fills data.
            ret.Fill(data);

            // returns.
            return ret;
        }

        /// <summary>
        /// Creates a matrix by specifying the data provider.
        /// </summary>
        /// <param name="provider">The data provider.</param>
        /// <returns>The new matrix.</returns>
        public static Matrix<T> CreateMatrix(IDataProvider<T> provider)
        {
            return new VirtualMatrix<T>(provider);
        }

        #endregion

        #region conversion

        /// <summary>
        /// Performs an implicit conversion from T[][] to Matrix.
        /// </summary>
        /// <param name="data">The matrix data.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Matrix<T>(T[][] data)
        {
            return new Matrix<T>(data);
        }

        /// <summary>
        /// Performs an implicit conversion from Matrix to T[][].
        /// </summary>
        /// <param name="matrix">The matrix.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator T[][](Matrix<T> matrix)
        {
            return matrix.data;
        }

        #endregion

        #region Special Matrix

        /// <summary>
        /// Gets the unit matrix with the specified width and height.
        /// </summary>
        /// <param name="rows">The number of rows.</param>
        /// <returns>The uint matrix.</returns>
        public static Matrix<T> Ones(int rows)
        {
            // gets the unit value of the type T.
            T unit = TypeConverter.Unit<T>();

            // creates the Matrix.
            Matrix<T> ret = Matrix<T>.CreateMatrix(rows, rows);

            // sets the values.
            for (int j = 0; j < ret.rows; j++)
            {
                for (int i = 0; i < ret.columns; i++)
                {
                    ret[i, j] = unit;
                }
            }

            // returns the value.
            return ret;
        }

        /// <summary>
        /// Returns the zero matrix with the specified rows and columns.
        /// </summary>
        /// <param name="rows">The number of rows.</param>
        /// <param name="columns">The number of columns.</param>
        /// <returns>The zero matrix.</returns>
        public static Matrix<T> Zeros(int rows, int columns)
        {
            // creates the Matrix.
            Matrix<T> ret = Matrix<T>.CreateMatrix(rows, columns);

            // sets the values.
            for (int j = 0; j < ret.rows; j++)
            {
                for (int i = 0; i < ret.columns; i++)
                {
                    ret[i, j] = default(T);
                }
            }

            // returns the value.
            return ret;
        }

        /// <summary>
        /// Returns the zero matrix with the specified rows and columns.
        /// </summary>
        /// <param name="rows">The number of rows and columns.</param>
        /// <returns>The zero matrix</returns>
        public static Matrix<T> Zeros(int rows)
        {
            return Matrix<T>.Zeros(rows, rows);
        }

        /// <summary>
        /// Creates the unit matrix.
        /// </summary>
        /// <param name="rows">The number of rows and columns.</param>
        /// <returns>The unit matrix.</returns>
        public static Matrix<T> Unit(int rows)
        {
            // gets the unit value of the type T.
            T unit = TypeConverter.Unit<T>();

            // creates the Matrix.
            Matrix<T> ret = Matrix<T>.CreateMatrix(rows, rows);

            // sets the values.
            for (int j = 0; j < ret.rows; j++)
            {
                for (int i = 0; i < ret.columns; i++)
                {
                    if (i == j)
                    {
                        ret[i, j] = unit;
                    }
                    else
                    {
                        ret[i, j] = default(T);
                    }
                }
            }

            // returns the value.
            return ret;
        }

        /// <summary>
        /// Makes a square with its diagonal values specified.
        /// </summary>
        /// <param name="diagValues">The diagonal values.</param>
        /// <returns>The square matrix.</returns>
        public static Matrix<T> Diag(T[] diagValues)
        {
            // checks the data.
            if (diagValues == null || diagValues.Length <= 0)
            {
                throw new MatrixException("The length of diagnal array < 0");
            }

            // allocates memory.
            int rows = diagValues.Length;
            Matrix<T> ret = Matrix<T>.CreateMatrix(rows, rows);

            // Fills the data to the diagonal position of the matrix.
            for (int j = 0; j < rows; j++)
            {
                for (int i = 0; i < rows; i++)
                {
                    if (i == j)
                    {
                        ret[i, j] = diagValues[i];
                    }
                }
            }

            // Returns the result.
            return ret;
        }

        #endregion

        #region Arithmetic Operators

        /// <summary>
        /// Implements the operator + (Addition).
        /// </summary>
        /// <param name="operand">The operand.</param>
        /// <param name="increase">The increase.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator +(Matrix<T> operand, T increase)
        {
            // Allocates memorty
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand.rows, operand.columns);

            // Computes the subtraction
            for (int j = 0; j < ret.rows; j++)
            {
                ret[j] = Operator.Addition<T, T, T>(operand[j], increase);
            }

            // Returns result.
            return ret;
        }

        /// <summary>
        /// Implements the operator + (Addition).
        /// </summary>
        /// <param name="increase">The increase.</param>
        /// <param name="operand">The operand.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator +(T increase, Matrix<T> operand)
        {
            return operand + increase;
        }

        /// <summary>
        /// Implements the operator + (Addition).
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator +(Matrix<T> operand1, Matrix<T> operand2)
        {
            // Checks dimensions.
            if (operand1.columns != operand2.columns || operand1.rows != operand2.rows)
            {
                throw new MatrixException("Dimensions of {0} and {1} do not agree.", operand1.ToString(), operand2.ToString());
            }

            // Allocates memorty
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand1.rows, operand1.columns);

            // Computes the subtraction
            for (int j = 0; j < ret.rows; j++)
            {
                ret[j] = Operator.Addition<T, T, T>(operand1[j], operand2[j]);
            }

            // Returns result.
            return ret;
        }

        /// <summary>
        /// Implements the operator - (Negation operator).  
        /// </summary>
        /// <param name="operand">The operand.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator -(Matrix<T> operand)
        {
            // Allocates memory.
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand.rows, operand.columns);

            // Fills data.
            for (int j = 0; j < operand.columns; j++)
            {
                ret[j] = Operator.Negative<T, T>(operand[j]);
            }

            // Returns result.
            return ret;
        }

        /// <summary>
        /// Implements the operator - (Subtraction).
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator -(Matrix<T> operand1, Matrix<T> operand2)
        {
            // Checks dimensions.
            if (operand1.columns != operand2.columns || operand1.rows != operand2.rows)
            {
                throw new MatrixException("Dimesions of {0} and {1} do not agree.", operand1.ToString(), operand2.ToString());
            }

            // Allocates memorty
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand2.rows, operand1.columns);

            // Computes the subtraction
            for (int j = 0; j < ret.rows; j++)
            {
                ret[j] = Operator.Subtraction<T, T, T>(operand1[j], operand2[j]);
            }

            // Returns result.
            return ret;
        }

        /// <summary>
        /// Implements the operator * (Scalar Multiple).
        /// </summary>
        /// <param name="operand">The operand1.</param>
        /// <param name="scalar">The factor.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator *(Matrix<T> operand, T scalar)
        {
            // Allocates memory.
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand.rows, operand.columns);

            // Fills data.
            for (int j = 0; j < operand.columns; j++)
            {
                ret[j] = Operator.Multiply<T, T, T>(operand[j], scalar);
            }

            // Returns result.
            return ret;
        }

        /// <summary>
        /// Implements the operator * (Scalar Multiple).
        /// </summary>
        /// <param name="scalar">The scalar.</param>
        /// <param name="operand">The operand.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator *(T scalar, Matrix<T> operand)
        {
            return operand * scalar;
        }

        /// <summary>
        /// Implements the operator * (Muliple).
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator *(Matrix<T> operand1, Matrix<T> operand2)
        {
            // Checks dimensions.
            if (operand1.columns != operand2.rows)
            {
                throw new MatrixException("Dimesions of {0} and {1} do not agree.", operand1.ToString(), operand2.ToString());
            }

            // Allocates memorty
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand1.rows, operand2.columns);

            // Computes the subtraction
            for (int j = 0; j < ret.rows; j++)
            {
                for (int i = 0; i < ret.columns; i++)
                {
                    T[] arr = Operator.Multiply<T, T, T>(operand1[j], operand2.GetColumn(i));
                    ret[i, j] = Operator.Sum<T, T>(arr);
                }
            }

            // Returns result.
            return ret;
        }

        /// <summary>
        /// Implements the operator / (Scalar Division).
        /// </summary>
        /// <param name="operand">The operand.</param>
        /// <param name="scalar">The scalar.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator /(Matrix<T> operand, T scalar)
        {
            // Allocates memory.
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand.rows, operand.columns);

            // Fills data.
            for (int j = 0; j < operand.columns; j++)
            {
                ret[j] = Operator.Division<T, T, T>(operand[j], scalar);
            }

            // Returns result.
            return ret;
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="scalar">The scalar.</param>
        /// <param name="operand">The operand.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator /(T scalar, Matrix<T> operand)
        {
            return operand.Inverse() * scalar;
        }

        /// <summary>
        /// Implements the operator / (Division).
        /// </summary>
        /// <param name="operand1">The operand1.</param>
        /// <param name="operand2">The operand2.</param>
        /// <returns>The result of the operator.</returns>
        public static Matrix<T> operator /(Matrix<T> operand1, Matrix<T> operand2)
        {
            // Checks dimensions.
            if (operand1.columns != operand2.rows)
            {
                throw new MatrixException("Dimesions of {0} and {1} do not agree.", operand1.ToString(), operand2.ToString());
            }

            // Allocates memorty
            Matrix<T> ret = Matrix<T>.CreateMatrix(operand1.rows, operand2.columns);

            // Uses the inverse of a matrix.
            return operand1 * operand2.Inverse();
        }

        #endregion

        #region Primary Change

        /// <summary>
        /// Exchanges the value of two rows.
        /// </summary>
        /// <param name="i">The <c>i</c>th row.</param>
        /// <param name="j">The <c>j</c>th row.</param>
        public void Exchange(int i, int j)
        {
            // checks the indices.
            if (i < 0 || i >= this.rows || j < 0 || j >= this.rows)
            {
                throw new MatrixException("Requires Row [{0}] and Row [{1}] from {2}.", i, j, this.ToString());
            }

            // exchange the row.
            T[] swap = this[i];
            this[i] = this[j];
            this[j] = swap;
        }

        /// <summary>
        /// Multiply the specified row with a scalar.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="scalar">The multiple.</param>
        public void Multiply(int rowIndex, T scalar)
        {
            // checks the index.
            if (rowIndex < 0 || rowIndex >= this.rows)
            {
                throw new MatrixException("Requires row [{0}] from {0}.", rowIndex, this.ToString());
            }

            // computes the multiple
            this[rowIndex] = Operator.Multiply<T, T, T>(this[rowIndex], scalar);
        }

        /// <summary>
        /// Multiply the a row with a scalar and adds it to anoterh row.
        /// </summary>
        /// <param name="i">The row adds the multiply result.</param>
        /// <param name="j">The row multiplies the scalar.</param>
        /// <param name="scalar">The scalar.</param>
        public void MultiplyAdd(int i, int j, T scalar)
        {
            // checks the indices.
            if (i < 0 || i >= this.rows || j < 0 || j >= this.rows)
            {
                throw new MatrixException("Requires row [{0}] and [{1}] from {2}.", i, j, this.ToString());
            }

            // computes the muliple and addition.
            this[i] = Operator.Addition<T, T, T>(this[i], Operator.Multiply<T, T, T>(this[j], scalar));
        }

        #endregion

        #region Utilities Methods

        /// <summary>
        /// Gets the pivot.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <returns>The pivot.</returns>
        public int Pivot(int rowIndex)
        {
            int index = rowIndex;

            for (int j = rowIndex + 1; j < this.rows; j++)
            {
                // Gets the row index of the maximum value in that column.
                IComparable<T> tmp = this[rowIndex, j] as IComparable<T>;
                if (tmp.CompareTo(this[rowIndex, index]) > 0)
                {
                    index = j;
                }
            }

            return index;
        }

        /// <summary>
        /// Transposes this instance.
        /// </summary>
        /// <returns>The tanspose of this Matrix.</returns>
        public Matrix<T> Transpose()
        {
            // Allocates memory.
            Matrix<T> ret = Matrix<T>.CreateMatrix(this.columns, this.rows);

            // Transposes the data.
            for (int j = 0; j < ret.rows; j++)
            {
                for (int i = 0; i < ret.columns; i++)
                {
                    ret[i, j] = this[j, i];
                }
            }

            // Returns the result.
            return ret;
        }

        /// <summary>
        /// Computes the inverse of this instance (using primary change).
        /// </summary>
        /// <returns>The inverse of this instance.</returns>
        public Matrix<T> Inverse()
        {
            // Checks the dimensions.
            if (!this.IsSquare)
            {
                throw new MatrixException("{0} is not a square to apply the Inverse operation.", this.ToString());
            }

            // gets the unit value of the type T.
            T unit = TypeConverter.Unit<T>();

            // gets a copy of this instance.
            Matrix<T> tmp = this.Clone();

            // Creates the uint matrix.
            Matrix<T> rst = Matrix<T>.Unit(this.columns);

            for (int j = 0; j < this.rows; j++)
            {
                int maxIndex = tmp.Pivot(j);

                // the determinant of this matrix is zero.
                IComparable<T> tv = tmp[j, maxIndex] as IComparable<T>;
                if (tv == null || tv.CompareTo(default(T)) == 0)
                {
                    throw new MatrixException("The dominant of {0} is zero.", this.ToString());
                }

                if (maxIndex != j)
                {
                    tmp.Exchange(j, maxIndex);
                    rst.Exchange(j, maxIndex);
                }

                // Gets the max value of column j.
                T scalar = Operator.Division<T, T, T>(unit, tmp[j, j]);

                // primary multiply.
                rst.Multiply(j, scalar);
                tmp.Multiply(j, scalar);

                for (int k = j + 1; k < this.rows; k++)
                {
                    T mul = Operator.Negative<T, T>(tmp[j, k]);
                    tmp.MultiplyAdd(k, j, mul);
                    rst.MultiplyAdd(k, j, mul);
                }
            }

            for (int j = this.rows - 1; j > 0; j--)
            {
                for (int k = j - 1; k >= 0; k--)
                {
                    T mul = Operator.Negative<T, T>(tmp[j, k]);
                    tmp.MultiplyAdd(k, j, mul);
                    rst.MultiplyAdd(k, j, mul);
                }
            } // end for

            // Returns the result.
            return rst;
        }

        #endregion

        #region Data Filling and Getting

        /// <summary>
        /// Gets the specified column.
        /// </summary>
        /// <param name="x">The column index.</param>
        /// <returns>The column data.</returns>
        public virtual T[] GetColumn(int x)
        {
            T[] ret = new T[this.rows];

            for (int j = 0; j < this.rows; j++)
            {
                ret[j] = this[x, j];
            }

            return ret;
        }

        /// <summary>
        /// Sets the data to the specified column.
        /// </summary>
        /// <param name="x">The column index.</param>
        /// <param name="data">The column data.</param>
        public virtual void SetColumn(int x, T[] data)
        {
            if (data.Length != this.rows)
            {
                throw new MatrixException("The data count {0} of input is not matchable to the Matrix {1}", data.Length, this.ToString());
            }

            for (int j = 0; j < this.rows; j++)
            {
                this[x, j] = data[j];
            }
        }

        /// <summary>
        /// Converts the Matrix data to an one dimension array.
        /// </summary>
        /// <returns>The one dimension array</returns>
        public T[] ToArray()
        {
            T[] array = new T[this.columns * this.rows];

            for (int h = 0; h < this.rows; h++)
            {
                int rowOff = h * this.columns;
                for (int w = 0; w < this.columns; w++)
                {
                    array[rowOff + w] = this[w, h];
                }
            }

            return array;
        }

        /// <summary>
        /// Converts the Matrix data to a two dimension array.
        /// </summary>
        /// <returns>The two dimension array</returns>
        public T[,] ToArray2()
        {
            T[,] array = new T[this.rows, this.columns];

            for (int h = 0; h < this.rows; h++)
            {
                for (int w = 0; w < this.columns; w++)
                {
                    array[h, w] = this[w, h];
                }
            }

            return array;
        }

        /// <summary>
        /// Converts the Matrix data to a cross array.
        /// </summary>
        /// <returns>The cross array</returns>
        public virtual T[][] ToCrossArray()
        {
            return this.data;
        }

        /// <summary>
        /// Fills the specified data.
        /// </summary>
        /// <param name="data">The data to be filled.</param>
        public void Fill(T[] data)
        {
            if (data.Length != this.rows * this.columns)
            {
                throw new MatrixException("The size of {0} and array [{1}] do not meet.", this.ToString(), data.Length);
            }

            for (int h = 0; h < this.rows; h++)
            {
                int rowOff = h * this.columns;
                for (int w = 0; w < this.columns; w++)
                {
                    this[w, h] = data[rowOff + w];
                }
            }
        }

        /// <summary>
        /// Fills the specified data.
        /// </summary>
        /// <param name="data">The data to be filled.</param>
        public void Fill(T[,] data)
        {
            if (data.GetLength(0) != this.rows || data.GetLength(1) != this.columns)
            {
                throw new MatrixException("The size of {0} and array [{1} {2}] do not meet.", this.ToString(), data.GetLength(0), data.GetLength(1));
            }

            for (int h = 0; h < this.rows; h++)
            {
                for (int w = 0; w < this.columns; w++)
                {
                    this[w, h] = data[h, w];
                }
            }
        }

        /// <summary>
        /// Fills the specified data.
        /// </summary>
        /// <param name="data">The data to be filled.</param>
        public virtual void Fill(T[][] data)
        {
            if (data.Length != this.rows || data[0].Length != this.columns)
            {
                throw new MatrixException("The size of {0} and array [{1} {2}] do not meet.", this.ToString(), data.Length, data[0].Length);
            }

            this.data = data;
        }

        /// <summary>
        /// Gets the data of this instance in a bounding box (The referenc coordinate is the left-top point).
        /// </summary>
        /// <param name="left">The left side of the bounding box.</param>
        /// <param name="top">The top side of the bounding box.</param>
        /// <param name="right">The right side of the bounding box.</param>
        /// <param name="bottom">The bottom side of the bounding box.</param>
        /// <returns>The data in the bounding box.</returns>
        public virtual Matrix<T> GetChild(int left, int top, int right, int bottom)
        {
            // Makes sure the left is less than right.
            if (left > right)
            {
                int swap = right;
                right = left;
                left = swap;
            }

            // Makes sure the top is less than the bottom.
            if (top > bottom)
            {
                int swap = bottom;
                bottom = top;
                top = swap;
            }

            // Makes sure the boundings is not out of range.
            if (left < 0 || left >= this.columns || right >= this.columns || top < 0 || top >= this.rows || bottom >= this.rows)
            {
                throw new MatrixException("Requires [{0}, {1}] ~ [{2}, {3}] from {4}.", left, top, right, bottom, this.ToString());
            }

            // Allocates memory.
            Matrix<T> ret = Matrix<T>.CreateMatrix(bottom - top, right - left);

            // Fills the data.
            for (int j = 0; j < ret.rows; j++)
            {
                for (int i = 0; i < ret.columns; i++)
                {
                    ret[i, j] = this[i + left, j + top];
                }
            }

            // Returns the result.
            return ret;
        }

        /// <summary>
        /// Gets the data of this instance in a bounding box (The referenc coordinate is the left-top point).
        /// </summary>
        /// <param name="left">The left side of the bounding box.</param>
        /// <param name="top">The top side of the bounding box.</param>
        /// <param name="right">The right side of the bounding box.</param>
        /// <param name="bottom">The bottom side of the bounding box.</param>
        /// <param name="dstWidth">Width of the destination region.</param>
        /// <param name="dstHeight">Height of the destionation region.</param>
        /// <returns>The data in the bounding box.</returns>
        public virtual Matrix<T> GetChild(int left, int top, int right, int bottom, int dstWidth, int dstHeight)
        {
            // Makes sure the left is less than right.
            if (left > right)
            {
                int swap = right;
                right = left;
                left = swap;
            }

            // Makes sure the top is less than the bottom.
            if (top > bottom)
            {
                int swap = bottom;
                bottom = top;
                top = swap;
            }

            // Makes sure the boundings is not out of range.
            if (left < 0 || left >= this.columns || right >= this.columns || top < 0 || top >= this.rows || bottom >= this.rows)
            {
                throw new MatrixException("Requires [{0}, {1}] ~ [{2}, {3}] from {4}: index is out of range.", left, top, right, bottom, this.ToString());
            }

            if (dstWidth <= 0 || dstHeight <= 0)
            {
                throw new MatrixException("Width [{0}] or height [{1}] is invalid.", dstWidth, dstHeight);
            }

            // Allocates memory.
            Matrix<T> ret = Matrix<T>.CreateMatrix(dstHeight, dstWidth);

            double xstep = (double)(right - left) / dstWidth, rowStep = (double)(bottom - top) / dstHeight;

            // Fills the data.
            for (int j = 0; j < dstHeight; j++)
            {
                double xinc = 0, yinc = j * rowStep;

                for (int i = 0; i < dstWidth; i++)
                {
                    xinc += xstep;
                    ret[i, j] = this[left + (int)xinc, top + (int)yinc];
                }
            }

            // Returns the result.
            return ret;
        }

        /// <summary>
        /// Sets the data in a matrix to the position of this instance.
        /// </summary>
        /// <param name="left">The left side.</param>
        /// <param name="top">The top side.</param>
        /// <param name="source">The source data.</param>
        public virtual void SetChild(int left, int top, Matrix<T> source)
        {
            int w = source.columns;
            int h = source.rows;

            // Checks the range.
            if (left < 0 || (w + left) > this.columns || top < 0 || (h + top) > this.rows)
            {
                throw new MatrixException("Requires [{0}, {1}] ~ [{2}, {3}] from {4}.", left, w + left, top, h + top, this.ToString());
            }

            // Sets the data.
            for (int j = 0; j < h; j++)
            {
                for (int i = 0; i < w; i++)
                {
                    this[i + left, j + top] = source[i, j];
                }
            }
        }

        /// <summary>
        /// Sets the data in a matrix to the position of this instance.
        /// </summary>
        /// <param name="left">The left side.</param>
        /// <param name="top">The top side.</param>
        /// <param name="dstWidth">Width of in this matrix.</param>
        /// <param name="dstHeight">Height of in this matrix.</param>
        /// <param name="source">The source data.</param>
        public virtual void SetChild(int left, int top, int dstWidth, int dstHeight, Matrix<T> source)
        {
            // Checks the range.
            if (left < 0 || (dstWidth + left) > this.columns || top < 0 || (dstHeight + top) > this.rows)
            {
                throw new MatrixException("Requires [{0}, {1}] ~ [{2}, {3}] from {4}.", left, dstWidth + left, top, dstHeight + top, this.ToString());
            }

            double xstep = (double)dstWidth / source.columns, rowStep = (double)dstHeight / source.rows;

            // Fills the data.
            for (int j = 0; j < source.rows; j++)
            {
                double xinc = 0, yinc = j * rowStep;

                for (int i = 0; i < source.columns; i++)
                {
                    xinc += xstep;
                    this[left + (int)xinc, top + (int)yinc] = source[i, j];
                }
            }
        }

        /// <summary>
        /// Allocates memory for Matrix data.
        /// </summary>
        public virtual void Allocate()
        {
            this.data = new T[this.rows][];

            for (int h = 0; h < this.rows; h++)
            {
                this.data[h] = new T[this.columns];
            }
        }

        #endregion

        #region Misc Operations

        /// <summary>
        /// Creates the compatible matrix (the size are equal).
        /// </summary>
        /// <returns>The compatible matrix</returns>
        public Matrix<T> CreateCompatibleMatrix()
        {
            Matrix<T> ret = Matrix<T>.CreateMatrix(this.rows, this.columns);
            return ret;
        }
        
        /// <summary>
        /// Creates a copy of this instance.
        /// </summary>
        /// <returns>The new copy of this instance.</returns>
        public Matrix<T> Clone()
        {
            Matrix<T> copy = Matrix<T>.CreateMatrix(this.rows, this.columns);

            for (int j = 0; j < this.rows; j++)
            {
                copy[j] = (T[])this[j].Clone();
            }

            return copy;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Matrix<{0}>({1},{2})", typeof(T).ToString(), this.columns, this.rows);
        }

        #endregion
    }
}
